/**
 * (created at 2011-1-27)
 */
package qiushuo.mysqlparser.recognizer.parser;

import static qiushuo.mysqlparser.recognizer.SQLTokenType.IDENTIFIER;
import static qiushuo.mysqlparser.recognizer.SQLTokenType.KW_REPLACE;

import java.sql.SQLSyntaxErrorException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import qiushuo.mysqlparser.ast.expression.Expression;
import qiushuo.mysqlparser.ast.statement.DMLReplaceStatement;
import qiushuo.mysqlparser.recognizer.SQLToken;
import qiushuo.mysqlparser.recognizer.TokenReader;

/**
 * @author <a href="mailto:QiuShuo1985@gmail.com">QIU Shuo</a>
 */
public class DMLReplaceStmtParser extends DMLInsertReplaceParser {
    public DMLReplaceStmtParser(TokenReader input) {
        super(input);
    }

    /**
     * nothing has been pre-consumed <code><pre>
     * 'REPLACE' ('LOW_PRIORITY' | 'DELAYED')? ('INTO')? tableName
     *  (  'SET' colName ('='|':=') (expr|'DEFAULT') (',' colName ('='|':=') (expr|'DEFAULT'))*
     *   | '(' (  colName (','colName)* ')' (  '(' 'SELECT' ... ')'
     *                                       | 'SELECT' ...
     *                                       |('VALUES'|'VALUE') value ( ',' value )*
     *                                      )
     *          | 'SELECT' ... ')'
     *         )
     *   | 'SELECT' ...
     *   |('VALUES'|'VALUE') value ( ',' value )*
     *  )
     * value := '(' (expr|'DEFAULT') ( ',' (expr|'DEFAULT'))* ')'
     * </pre></code>
     */
    public DMLReplaceStatement replaceStmt() throws SQLSyntaxErrorException {
        match(KW_REPLACE);
        int mode = DMLReplaceStatement.LDH_UNDEF;
        SQLToken token;
        String tableName;
        loop1: while (true) {
            token = input.next();
            if (token == null) throw err("unexpected EOF after INSERT");
            switch (token.getType()) {
            case KW_LOW_PRIORITY:
                if (mode != DMLReplaceStatement.LDH_UNDEF) err("duplicated mode for replace: " + token);
                mode = DMLReplaceStatement.LDH_LOW;
                continue;
            case KW_DELAYED:
                if (mode != DMLReplaceStatement.LDH_UNDEF) err("duplicated mode for replace: " + token);
                mode = DMLReplaceStatement.LDH_DELAY;
                continue;
            case KW_INTO:
                token = match(IDENTIFIER);
                tableName = token.getText();
                break loop1;
            case IDENTIFIER:
                tableName = token.getText();
                break loop1;
            default:
                throw err("unexpected token after INSERT: " + token);
            }
        }
        // -------- after table name-----------------------------------------------
        List<String> columnNameList = null;
        List<List<Expression>> valuesList = null;
        Expression select = null;
        token = input.next();
        switch (token.getType()) {
        case KW_SET:
            columnNameList = new LinkedList<String>();
            valuesList = new ArrayList<List<Expression>>(1);
            setColumnsFormat(columnNameList, valuesList, false);
            return new DMLReplaceStatement(mode, tableName, columnNameList, valuesList);
        case PUNC_LEFT_PAREN:
            token = input.next();
            if (token == null) err("unexpected EOF for REPLACE");
            switch (token.getType()) {
            case IDENTIFIER:
                columnNameList = new LinkedList<String>();
                columnNameList.add(token.getText());
                columnList(columnNameList);
                if ((token = input.next()) == null) throw err("unexpected EOF after REPLACE");
                switch (token.getType()) {
                case PUNC_LEFT_PAREN:
                case KW_SELECT:
                    input.backForce();
                    select = select();
                    return new DMLReplaceStatement(mode, tableName, columnNameList, select);
                case KW_VALUES:
                    valuesList = new LinkedList<List<Expression>>();
                    valuesList(valuesList, false);
                    return new DMLReplaceStatement(mode, tableName, columnNameList, valuesList);
                case IDENTIFIER:
                    if ("VALUE".equals(token.getText().toUpperCase())) {
                        valuesList = new LinkedList<List<Expression>>();
                        valuesList(valuesList, false);
                        return new DMLReplaceStatement(mode, tableName, columnNameList, valuesList);
                    }
                default:
                    throw err("unexpected token in REPLACE: " + token);
                }
            case KW_SELECT:
                input.skipForce(-2);
                select = select();
                return new DMLReplaceStatement(mode, tableName, null, select);
            default:
                throw err("unexpected token after REPLACE table '(': " + token);
            }
        case KW_SELECT:
            input.backForce();
            select = select();
            return new DMLReplaceStatement(mode, tableName, null, select);
        case KW_VALUES:
            valuesList = new LinkedList<List<Expression>>();
            valuesList(valuesList, false);
            return new DMLReplaceStatement(mode, tableName, columnNameList, valuesList);
        case IDENTIFIER:
            if ("VALUE".equals(token.getText().toUpperCase())) {
                valuesList = new LinkedList<List<Expression>>();
                valuesList(valuesList, false);
                return new DMLReplaceStatement(mode, tableName, columnNameList, valuesList);
            }
        default:
            throw err("unexpected token for REPLACE: " + token);
        }
    }

}
